#ifndef _UTIL_H__
#define _UTIL_H__

#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

/* NOLINTNEXTLINE(bugprone-reserved-identifier) */
#define __unused __attribute__((__unused__)) 

#define LEN(S) (sizeof(S) / sizeof(*(S)))

#define N_OR_X(N, X) (!!(N) ? (N) : (X))
#define N_OR_1(N)    N_OR_X(N, !(N))

#define STRINGIFY_AUX(X) #X
#define STR(X)		 STRINGIFY_AUX(X)

#define STREQ(A, B)	(!strcmp((A), (B)))
#define STRNEQ(A, B, N) (!strncmp((A), (B), (N)))

#define SAFE_ASSIGN(TO, FROM) SAFE_OP(TO, =, FROM)
#define SAFE_OP(TO, OP, FROM) ((TO)OP(__typeof__((TO)))(FROM))

#define UNREACHABLE(...) assert(0 && "reached unreachable")

#if defined(DEBUG) || defined(__clang_analyzer__)
#define DEBUG_ASSERT(...) assert(__VA_ARGS__)
#else
#define DEBUG_ASSERT(...) NULL
#endif

#define _STR2INT__(VAR, STR, MAX, ERR, TYPE, SIZE)                            \
	do {                                                                  \
		char*	 _e = NULL;                                           \
		TYPE int _n; /* NOLINT(bugprone-macro-parentheses) */         \
		errno = 0;                                                    \
		if ((_n = strto##SIZE((STR), &_e, 0)) > (MAX))                \
			errno = ERANGE;                                       \
		if (errno || *_e)                                             \
			ERR; /* NOLINT(bugprone-macro-parentheses) */         \
		SAFE_ASSIGN(VAR, _n);                                         \
	} while (0)

#define STR2INT(VAR, STR, MAX, ERR) _STR2INT__(VAR, STR, MAX, ERR, long, l)
#define STR2UINT(VAR, STR, MAX, ERR)                                          \
	_STR2INT__(VAR, STR, MAX, ERR, unsigned long, ul)

enum EXIT_STATUSES {
	/* for program which uses `exec` */
	EXIT_TIMEDOUT = 124,	  /* Time expired before child completed. */
	EXIT_CANCELED = 125,	  /* Internal error prior to exec attempt. */
	EXIT_CANNOT_INVOKE = 126, /* Program located, but not usable. */
	EXIT_ENOENT = 127,	  /* Could not find program to exec. */
};

#define NO_MEMORY(...) err(!0, "failed to allocate memory")

#endif /* _UTIL_H__ */
